﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Linq;

namespace HigLabo.Net.SugarSync
{
    /// <summary>
    /// 
    /// </summary>
    public partial class SugarSyncClient : HttpClient
    {
        public static readonly SugarSyncClientDefaultSettings Default = new SugarSyncClientDefaultSettings();
        private String _AuthorizationToken = "";
        private readonly String _UserName = Default.UserName;
        private readonly String _Password = Default.Password;
        private readonly String _AccessKey = Default.AccessKey;
        private readonly String _PrivateAccessToken = Default.PrivateAccessToken;

        /// <summary>
        /// 
        /// </summary>
        public SugarSyncClient()
            : this(Default.UserName, Default.Password, Default.AccessKey, Default.PrivateAccessToken)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <param name="accessKey"></param>
        /// <param name="privateAccessToken"></param>
        public SugarSyncClient(String userName, String password, String accessKey, String privateAccessToken)
        {
            _UserName = userName;
            _Password = password;
            _AccessKey = accessKey;
            _PrivateAccessToken = privateAccessToken;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public void Authenticate(Action<Boolean> callback)
        {
            _AuthorizationToken = "";
            this.GetAuthorizationToken(token =>
            {
                if (token == null)
                {
                    callback(false);
                }
                _AuthorizationToken = token;
                callback(true);
            });
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public void GetAuthorizationToken(Action<String> callback)
        {
            var cm = CreateGetAuthorizationTokenCommand();
            GetResponse(cm, res => callback(res.Headers["Location"]));
        }
        private HttpRequestCommand CreateGetAuthorizationTokenCommand()
        {
            var sb = new StringBuilder(1024);
            var cm = new HttpRequestCommand(SugarSyncUrl.Authorization);
            cm.MethodName = HttpMethodName.Post;

            sb.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
            sb.AppendLine("<authRequest>");

            sb.Append("<username>");
            sb.Append(_UserName);
            sb.AppendLine("</username>");

            sb.Append("<password>");
            sb.Append(_Password);
            sb.AppendLine("</password>");

            sb.Append("<accessKeyId>");
            sb.Append(_AccessKey);
            sb.AppendLine("</accessKeyId>");

            sb.Append("<privateAccessKey>");
            sb.Append(_PrivateAccessToken);
            sb.AppendLine("</privateAccessKey>");

            sb.AppendLine("</authRequest>");

            cm.SetBodyStream(Encoding.UTF8.GetBytes(sb.ToString()));

            return cm;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public void GetUserInfo(Action<User> callback)
        {
            GetResponseBodyText(HttpMethodName.Get, SugarSyncUrl.User, null, xml => callback(new User(XElement.Parse(xml))));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void GetCollections(GetCollectionsCommand command, Action<CollectionContents> callback)
        {
            var d = command.GetXmlData();
            GetResponseBodyText(HttpMethodName.Get, HttpClient.CreateQueryString(command.Url + "/contents", d),
                xml => callback(new CollectionContents(XElement.Parse(xml))));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void GetWorkspace(GetWorkspacesCommand command, Action<Workspace> callback)
        {
            GetResponseBodyText(HttpMethodName.Get, command.Url, xml => callback(new Workspace(XElement.Parse(xml))));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void UpdateWorkspaceName(UpdateWorkspaceNameCommand command, Action<Workspace> callback)
        {
            GetResponseBodyText(HttpMethodName.Put, command.Url, command, xml => callback(new Workspace(XElement.Parse(xml))));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void GetFolder(GetFoldersCommand command, Action<Folder> callback)
        {
            GetResponseBodyText(HttpMethodName.Get, command.Url, xml => callback(new Folder(XElement.Parse(xml))));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void CreateFolder(CreateFoldersCommand command, Action<String> callback)
        {
            GetResponseBodyText(HttpMethodName.Post, command.Url, command, s => callback(s));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void DeleteFolder(DeleteFoldersCommand command, Action<String> callback)
        {
            GetResponseBodyText(HttpMethodName.Delete, command.Url, command, s => callback(s));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void UpdateFolderName(UpdateFolderNameCommand command, Action<String> callback)
        {
            GetResponseBodyText(HttpMethodName.Put, command.Url, command, s => callback(s));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void GetReceivedShares(GetReceivedSharesCommand command, Action<ReceivedShareCollection> callback)
        {
            GetResponseBodyText(HttpMethodName.Get, command.Url, xml => callback(new ReceivedShareCollection(XElement.Parse(xml))));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void GetFile(GetFileCommand command, Action<File> callback)
        {
            GetResponseBodyText(HttpMethodName.Get, command.Url, xml => callback(new File(XElement.Parse(xml)) { Ref = command.Url }));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void CreateFile(CreateFileCommand command, Action<String> callback)
        {
            GetResponseBodyText(HttpMethodName.Post, command.Url, command, s => callback(s));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void UpdateFile(UpdateFileCommand command, Action<File> callback)
        {
            GetResponseBodyText(HttpMethodName.Put, command.Url, command, xml => callback(new File(XElement.Parse(xml))));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void DeleteFile(DeleteFileCommand command, Action<String> callback)
        {
            GetResponseBodyText(HttpMethodName.Delete, command.Url, s => callback(s));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void CopyFile(CopyFileCommand command, Action<String> callback)
        {
            GetResponseBodyText(HttpMethodName.Post, command.Url, command, s => callback(s));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void GetFileData(GetFileDataCommand command, Action<byte[]> callback)
        {
            GetHttpWebResponse(HttpMethodName.Get, command.Url, callback);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void PutFileData(PutFileDataCommand command, Action<String> callback)
        {
            var cl = new HttpClient();
            var cm = new HttpRequestCommand(command.Url);
            var boundary = PutFileDataCommand.GetBoundaryString();
            cm.Headers["Authorization"] = _AuthorizationToken;
            cm.MethodName = HttpMethodName.Put;
            cm.ContentType = "multipart/form-data; boundary=\"" + boundary + "\"";
            cm.SetBodyStream(command.CreatePostData(boundary));
            cl.GetResponse(cm, res => callback(res.BodyText));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void GetVersionHistory(GetVersionHistoryCommand command, Action<FileVersions> callback)
        {
            GetResponseBodyText(HttpMethodName.Get, command.Url + "/version", xml => callback(new FileVersions(XElement.Parse(xml))));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void CreatePublicLink(PublicLinkCommand command, Action<File> callback)
        {
            if (callback == null) throw new ArgumentNullException("callback");
            command.PublicLink = true;
            GetResponseBodyText(HttpMethodName.Put, command.Url, command,
                xml => callback(new File(XElement.Parse(xml)) {Ref = command.Url}));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void DestroyPublicLink(PublicLinkCommand command, Action<File> callback)
        {
            command.PublicLink = false;
            GetResponseBodyText(HttpMethodName.Put, command.Url, command, xml => callback(new File(XElement.Parse(xml)) { Ref = command.Url }));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public void GetAlbum(GetAlbumsCommand command, Action<Albums> callback)
        {
            GetResponseBodyText(HttpMethodName.Get, command.Url, xml => callback(new Albums(XElement.Parse(xml)) { Ref = command.Url }));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void GetAlbumsCollectionContents(GetAlbumsCollectionCommand command, Action<CollectionContents> callback)
        {
            GetResponseBodyText(HttpMethodName.Get, command.Url, xml => callback(new CollectionContents(XElement.Parse(xml))));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="url"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        private void GetResponseBodyText(HttpMethodName methodName, String url, Action<String> callback)
        {
            GetResponseBodyText(methodName, url, null, callback);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="url"></param>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        private void GetResponseBodyText(HttpMethodName methodName, String url, SugarSyncCommand command, Action<String> callback)
        {
            var cl = new HttpClient();
            var cm = new HttpRequestCommand(url);

            cl.ResponseEncoding = Encoding.UTF8;

            cm.MethodName = methodName;
            cm.Headers["Authorization"] = _AuthorizationToken;
            if (command == null)
            {
                cl.GetResponse(cm, rs => callback(rs.BodyText));
            }
            else
            {
                var d = command.GetXmlData();
                cm.SetBodyStream(Encoding.UTF8.GetBytes(d.ToString()));
                cl.GetResponse(cm, rs => callback(rs.BodyText));
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="url"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        private void GetHttpWebResponse(HttpMethodName methodName, String url, Action<byte[]> callback)
        {
            GetHttpWebResponse(methodName, url, null, callback);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="url"></param>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        private void GetHttpWebResponse(HttpMethodName methodName, String url, SugarSyncCommand command, Action<byte[]> callback)
        {
            if (methodName == HttpMethodName.Put || methodName == HttpMethodName.Delete)
            {
                throw new ArgumentException();
            }
            var cl = new HttpClient();
            var cm = new HttpRequestCommand(url);
            cm.MethodName = methodName;
            cm.Headers["Authorization"] = _AuthorizationToken;
            if (command == null)
            {
                cl.GetHttpWebResponse(cm, rs => callback(rs.GetResponseStream().ToByteArray()));
            }
            else
            {
                var d = command.GetXmlData();
                cm.SetBodyStream(Encoding.UTF8.GetBytes(d.ToString()));
                cl.GetHttpWebResponse(cm, rs => callback(rs.GetResponseStream().ToByteArray()));
            }
        }
    }
}